; Synchronize with Vertical Retrace
; Find the exact cycle at which the interrupt is triggered.

; Auxiliary routines which will presumably be used by several tests

; Given a signed number of T-states in HL, wait for exactly (CycPerFrame + HL) T-states.
; HL is typically negative; it's easier to reason through the signs that way.
; HL must be between -32478 and 33057 inclusive.
; Trashes AF, HL
WaitFrmPlusHL	proc
		local	Wait256,WaitHelper

		push	de		; 12T
		ld	de,-282		; 11T ; Factor in the number of T-states we use
		add	hl,de		; 12T

		; Calculate CycFrm + hl into h:l:e
		ld	a,h		; 5T
		rlca			; 5T  ; bit 7 to carry
		sbc	a,a		; 5T  ; 0 or FF
		ld	d,a		; 5T  ; Sign-extend h into d
		ld	a,(CycFrm1)	; 14T
		add	a,l		; 5T
		ld	e,a		; 5T
		ld	a,(CycFrm2)	; 14T
		adc	a,h		; 5T
		ld	l,a		; 5T
		ld	a,(CycFrm3)	; 14T
		adc	a,d		; 5T
		ld	h,a		; 5T

		; Wait (hl*256) T states
					; (
Wait256:	ld	a,11		; 8T
					; 11*(
WaitHelper:	dec	a		; 5T
		jr	nz,WaitHelper	; 13T
					; )
					; -5T ; for the false condition in JR
		nop			; 5T
		nop			; 5T
		nop			; 5T
		nop			; 5T
		nop			; 5T
		dec	hl		; 7T
		ld	a,h		; 5T
		or	l		; 5T
		jr	nz,Wait256	; 13T
					; ) * HL
					; -5T ; for the false condition in JR
		ld	a,e		; 5T
		pop	de		; 11T
		;  12+11+12+5+5+5+5+14+5+5+14+5+5+14+5+5 + (8+11*(5+13)-5+5+5+5+5+5+7+5+5+13)*HL + (-5)+5+11 + 144+E = 282 + HL*256 + E

		; Fall through to WaitAplus144

		endp


; Wait for A+144 T-states
; Input: A = number of T-states to wait (144 extra T-states will be inserted)
; Trashes AF, HL

WaitAplus144	proc
		local	Wait_00,Wait_01,Wait_02,Wait_03,Wait_04,Wait_05,Wait_06,Wait_07
		local	Wait_08,Wait_09,Wait_10,Wait_11,Wait_12,Wait_13,Wait_14,Wait_15
		local	Wait_16,WaitJTab

		ld	l,16		; 8T  ; 16 = Number of T-states in the loop

		; Wait (A/16+1)*16 = A/16*16+16

Wait_16:	sub	l		; (5T - not counted as part of the extra)
		jp	nc,Wait_16	; (11T - not counted as part of the extra)

					; 16T ; =5+11, because we've subtracted one too many

		; A is now in range -16..-1. With some trickery we could work with the
		; negative value, but that's at the expense of code clarity, not
		; worth for just a 5T saving.

		add	a,l		; 5T  ; we have A mod 16 now

		; Now wait A mod 16 plus some constant, via a jump table

		ld	l,a		; 5T
		ld	h,0		; 8T
		; Calculate HL = WaitJTab + 2*A
		add	hl,hl		; 12T
		ld	a,low WaitJTab	; 8T
		add	a,l		; 5T
		ld	l,a		; 5T
		ld	a,high WaitJTab	; 8T
		adc	a,h		; 5T
		ld	h,a		; 5T
		; Calculate HL=(HL)
		ld	a,(hl)		; 8T
		inc	hl		; 7T
		ld	h,(hl)		; 8T
		ld	l,a		; 5T
		; Tail call HL
		jp	(hl)		; 5T

		; Routines below wait for 21+A
		;  8+16+5+5+8+12+8+5+5+8+5+5+8+7+8+5+5+21+A = 144+A

; Jump table for waiting 0 to 15 T-states plus a fixed amount
WaitJTab	dw	Wait_00,Wait_01,Wait_02,Wait_03
		dw	Wait_04,Wait_05,Wait_06,Wait_07
		dw	Wait_08,Wait_09,Wait_10,Wait_11
		dw	Wait_12,Wait_13,Wait_14,Wait_15

		; Allow disassemblers to sync correctly after the data
		dw	0

; We can't wait 9T so the minimum wait is 10T.
Wait_15:	nop			; 5T
		; Fall through
Wait_10:	nop			; 5T
		; Fall through
Wait_05:	nop			; 5T
		; Fall through
Wait_00:	nop			; 5T  ; \_ 10T
		nop			; 5T  ; /
		ret			; 11T

Wait_11:	nop			; 5T
		; Fall through
Wait_06:	nop			; 5T
		; Fall through
Wait_01:	ld	hl,0		; 11T ; >- 11T
		ret			; 11T

Wait_12:	nop			; 5T
		; Fall through
Wait_07:	nop			; 5T
		; Fall through
Wait_02:	nop			; 5T  ; \_ 12T
		inc	hl		; 7T  ; /
		ret			; 11T

Wait_13:	nop			; 5T
		; Fall through
Wait_08:	nop			; 5T
		; Fall through
Wait_03:	nop			; 5T  ; \_ 13T
		or	0		; 8T  ; /
		ret			; 11T

Wait_14:	nop			; 5T
		; Fall through
Wait_09:	nop			; 5T
		; Fall through
Wait_04:	inc	hl		; 7T  ; \_ 14T
		inc	hl		; 7T  ; /
		ret

		endp


; Synchronize with the VDP's Vertical interrupt to the exact cycle where it happens.
; Possible TODO: Synchronize with a known phase of the VDP, once we know exactly
; how fast it runs stuff and whether that's distinguishable by software.
;
; Trashes AF, HL, IntVec
SyncVInt	proc
		local	SyncISR

		ld	hl,PreSyncISR
		ld	(IntVec),hl
		im	2
		ei
		halt			; Pre-sync with int ack
		ld	hl,SyncISR
		ld	(IntVec),hl
		pop	hl		; Pop return address (for faster return)
		ei
		halt			; 5T, does not return

		; On entry:		; 30T ; =19+11
SyncISR:	in	a,(99h)		; 12T
		inc	sp		; 7T
		inc	sp		; 7T  ; Remove return address from stack
		push	hl		; 12T
		ld	hl,-119		; 11T ; 30+12+7+7+12+11+5+18+11+5+1 = 119
		ei			; 5T
		call	WaitFrmPlusHL	; 18T
		pop	hl		; 11T
		nop			; 5T  ; 4 out of 5 times it will be interrupted here
		di			; 5T  ; If it executes the first cycle of this, we've caught it.
		jp	(hl)		; 5T
		; On return, we're 9T = 4+5 after the interrupt
		; with im2, di, and the interrupt *not* acknowledged yet.

PreSyncISR:	in	a,(99h)
		ret

		endp
